package montecarlo;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.*;

public class MultiThreadMontCarlo
implements Callable<Double>
{
		//variables
		private	double S;
		private	double X;
		private	double r;
		private	double T;
		private	double b;
		private	double v;
		private	int nSteps;
		private	int nSimulations;
		private	String CallPutFlag;
		
		//for muti-threading
		private double blockingCoefficient=0.7;
		
		//Montecarlo function
		public double montecarlo() throws ExecutionException, InterruptedException{
			
			
			double dt;
			double Sum=0,Drift,vSqrdt;
			double z=0;
			
			dt=T/nSteps;
			Drift=(b-v*v/2)*dt;
			vSqrdt=v*Math.sqrt(dt);
			
			if(CallPutFlag.contains("c")){
				z=1;
			}
			else if(CallPutFlag.contains("p")){
				z=-1;
			}
							
				
				//determine poolSize
				int numberOfCores = Runtime.getRuntime().availableProcessors();
				int poolSize = (int)(numberOfCores / (1 - blockingCoefficient));
				
				//create queue process
					    List<Callable<Double>> partitions = new ArrayList<Callable<Double>>();
					    for(int i=0;i<nSimulations;i++) {
					      partitions.add(new MyCallable(S,Drift,vSqrdt));
					    }
					    
					  //this code wait to obtain all results before doing the next steps
					    ExecutorService executorPool = Executors.newFixedThreadPool(poolSize);    
					    List<Future<Double>> resultsPool = executorPool.invokeAll(
					      partitions, 10000, TimeUnit.SECONDS);
					    executorPool.shutdown();
			
		
					 
		for(Future<Double> list : resultsPool) {
		    	Sum=Sum+Math.max(z*(list.get()-X), 0);
			}			
			return Math.exp(-r*T)*(Sum/nSimulations);
		}
		
		public Double call()throws ExecutionException, InterruptedException {
			return Double.valueOf(montecarlo());
		}
		
		//Constructors 
		public MultiThreadMontCarlo(){	//default constructor
			S=40;
			X=50;
			r=0.06;
			T=0.5;
			b=0.1;
			v=0.45;
			nSteps=168;
			nSimulations=100000;
			CallPutFlag="c";
		}
		
		
		public MultiThreadMontCarlo(String CallPutFlag, double S, double X, 
				double T,double r,double b,double v, int nSteps,
				int nSimulations){
			this.S=S;
			this.X=X;
			this.r=r;
			this.T=T;
			this.b=v;
			this.v=v;
			this.nSteps=nSteps;
			this.nSimulations=nSimulations;
			this.CallPutFlag=CallPutFlag;
		}
		
		private class MyCallable 
		implements Callable<Double>{
			 private double Drift;
	    	  private double vSqrdt;
	    	  private double S;
	    	  
	    	  MyCallable(double S,double Drift, double vSqrdt){
	    		  this.S=S;
	    		  this.Drift=Drift;
	    		  this.vSqrdt=vSqrdt;
	    	  }
	    	  
	          public Double call() throws Exception {
	        	  double St=S;
	        	  
	  			
	  			//true random
	  			//Random rand = new Random(System.currentTimeMillis()); 
	        	//Random with Seed
	  			Random rand = new Random(10);
					for(int j=1;j<=nSteps;j++){
						St=St*Math.exp(Drift+vSqrdt*rand.nextGaussian());
					}
					return St;
	            }  
		}
	
}
